package com.yxh.lib_base.ext.viewmodel

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.liveData
import androidx.lifecycle.viewModelScope
import com.yxh.lib_base.util.LogUtils
import com.blankj.utilcode.util.ToastUtils
import com.yxh.lib_base.base.BaseActivity
import com.yxh.lib_base.base.BaseFragment
import com.yxh.lib_base.base.viewmodel.BaseViewModel
import com.yxh.lib_base.base.viewmodel.dismissLoading
import com.yxh.lib_base.base.viewmodel.showLoading
import com.yxh.lib_base.network.AppException
import com.yxh.lib_base.network.BaseResponse
import com.yxh.lib_base.network.ExceptionHandle
import com.yxh.lib_base.state.ResultState
import com.yxh.lib_base.state.paresException
import com.yxh.lib_base.state.paresResult
import kotlinx.coroutines.*

/**
 * 作者　: hegaojian
 * 时间　: 2020/4/8
 * 描述　:BaseViewModel请求协程封装
 */

/**
 * 显示页面状态，这里有个技巧，成功回调在第一个，其后两个带默认值的回调可省
 * @param resultState 接口返回值
 * @param onLoading 加载中
 * @param onSuccess 成功回调
 * @param onError 失败回调
 *
// */
fun <T> BaseActivity.parseState(
    resultState: ResultState<T>,
    onSuccess: (T) -> Unit,
    onError: ((AppException) -> Unit)? = null,
    onLoading: (() -> Unit)? = null,
) {
    when (resultState) {
        is ResultState.Loading -> {
            showLoading(resultState.loadingMessage)
            onLoading?.run { this }
        }
        is ResultState.Success -> {
            dismissLoading()
            onSuccess(resultState.data)
        }
        is ResultState.Error -> {
            dismissLoading()
            onError?.run { this(resultState.error) }
        }
    }
}

/**
 * 显示页面状态，这里有个技巧，成功回调在第一个，其后两个带默认值的回调可省
 * @param resultState 接口返回值
 * @param onLoading 加载中
 * @param onSuccess 成功回调
 * @param onError 失败回调
 *
 */
fun <T> BaseFragment.parseState(
    resultState: ResultState<T>,
    onSuccess: (T) -> Unit,
    onError: ((AppException) -> Unit)? = null,
    onLoading: ((message: String) -> Unit)? = null,
) {
    when (resultState) {
        is ResultState.Loading -> {
            if (onLoading == null) {
                showLoading(resultState.loadingMessage)
            } else {
                onLoading.invoke(resultState.loadingMessage)
            }
        }
        is ResultState.Success -> {
            dismissLoading()
            onSuccess(resultState.data)
        }
        is ResultState.Error -> {
            dismissLoading()
            onError?.run { this(resultState.error) }
        }
    }
}


/**
 * net request 不校验请求结果数据是否是成功
 * @param block 请求体方法
 * @param resultState 请求回调的ResultState数据
 * @param isShowDialog 是否显示加载框
 * @param loadingMessage 加载框提示内容
 */
fun <T> BaseViewModel.requestNetWork(
    block: suspend () -> BaseResponse<T>,
    resultState: MutableLiveData<ResultState<T>>,
    isShowDialog: Boolean = false,
    loadingMessage: String = "请求网络中...",
): Job {
    return viewModelScope.launch {
        runCatching {
            if (isShowDialog) resultState.value = ResultState.onAppLoading(loadingMessage)
            //请求体
            block()
        }.onSuccess {
            resultState.paresResult(it)
        }.onFailure {
            LogUtils.e(it.message ?: "")
            //打印错误栈信息
            it.printStackTrace()
            resultState.paresException(it)
        }
    }
}

/**
 * net request 不校验请求结果数据是否是成功
 * @param block 请求体方法
 * @param resultState 请求回调的ResultState数据
 * @param isShowDialog 是否显示加载框
 * @param loadingMessage 加载框提示内容
 */
fun <T> BaseViewModel.requestNoCheck(
    block: suspend () -> T,
    resultState: MutableLiveData<ResultState<T>>,
    isShowDialog: Boolean = false,
    loadingMessage: String = "请求网络中...",
): Job {
    return viewModelScope.launch {
        runCatching {
            if (isShowDialog) resultState.value = ResultState.onAppLoading(loadingMessage)
            //请求体
            block()
        }.onSuccess {
            resultState.paresResult(it)
        }.onFailure {
            LogUtils.e(it)
            //打印错误栈信息
            it.printStackTrace()
            resultState.paresException(it)
        }
    }
}

/**
 * 过滤服务器结果，失败抛异常
 * @param block 请求体方法，必须要用suspend关键字修饰
 * @param success 成功回调
 * @param error 失败回调 可不传
 * @param isShowLoading 是否显示加载框
 * @param loadingMessage 加载框提示内容
 */
fun <T, R> BaseViewModel.requestLiveData(
    block: suspend () -> BaseResponse<T>,
    success: (T) -> R,
    onError: ((AppException) -> R)? = null,
    isShowLoading: Boolean = false,
    loadingMessage: String = "请求网络中...",
    isToast: Boolean = true
) = liveData<R> {
    runCatching {
        if (isShowLoading) showLoading(loadingMessage)
        //请求体
        block()
    }.onSuccess {
        //网络请求成功 关闭弹窗
        loadingChange.dismissDialog.postValue(false)
        runCatching {
            //校验请求结果码是否正确，不正确会抛出异常走下面的onFailure
            executeResponse(it) { t ->
                emit(success(t))
            }
        }.onFailure { e ->
            //打印错误消息
            LogUtils.e(e)
            //打印错误栈信息
            e.printStackTrace()
            onError?.invoke(ExceptionHandle.handleException(e))?.let {
                //失败回调
                emit(it)
            }
            if (isToast) {
                ToastUtils.showShort(e.message)
            }
        }
    }.onFailure {
        //网络请求异常 关闭弹窗
        loadingChange.dismissDialog.postValue(false)
        //打印错误消息
        LogUtils.e(it)
        //打印错误栈信息
        it.printStackTrace()
        //失败回调
        error(ExceptionHandle.handleException(it))
        if (isToast) {
            ToastUtils.showShort(it.message)
        }
    }
}


fun <T> BaseViewModel.requestNetWork(
    block: suspend () -> BaseResponse<T>,
    success: (T) -> Unit,
    error: (AppException) -> Unit = {},
    isShowDialog: Boolean = false,
    loadingMessage: String = "请求网络中...",
    isToast: Boolean = true,
): Job {
    //如果需要弹窗 通知Activity/fragment弹窗
    return viewModelScope.launch {
        runCatching {
            if (isShowDialog) loadingChange.showDialog.postValue(loadingMessage)
            //请求体
            block()
        }.onSuccess {
            //网络请求成功 关闭弹窗
            loadingChange.dismissDialog.postValue(false)
            runCatching {
                //校验请求结果码是否正确，不正确会抛出异常走下面的onFailure
                executeResponse(it) { t -> success(t) }
            }.onFailure { e ->
                //打印错误消息
                LogUtils.e(e)
                //打印错误栈信息
                e.printStackTrace()
                //失败回调
                error(ExceptionHandle.handleException(e))
                if (isToast) {
                    ToastUtils.showShort(e.message)
                }
            }
        }.onFailure {
            //网络请求异常 关闭弹窗
            loadingChange.dismissDialog.postValue(false)
            //打印错误消息
            LogUtils.e(it)
            //打印错误栈信息
            it.printStackTrace()
            //失败回调
            error(ExceptionHandle.handleException(it))
            if (isToast) {
                ToastUtils.showShort(it.message)
            }
        }
    }
}

/**
 *  不过滤请求结果
 * @param block 请求体 必须要用suspend关键字修饰
 * @param success 成功回调
 * @param error 失败回调 可不给
 * @param isShowDialog 是否显示加载框
 * @param loadingMessage 加载框提示内容
 */
fun <T> BaseViewModel.requestNoCheck(
    block: suspend () -> T,
    success: ((T) -> Unit)? = null,
    error: (AppException) -> Unit = {},
    isShowDialog: Boolean = false,
    loadingMessage: String = "请求网络中...",
): Job {
    //如果需要弹窗 通知Activity/fragment弹窗
    if (isShowDialog) loadingChange.showDialog.postValue(loadingMessage)
    return viewModelScope.launch {
        runCatching {
            //请求体
            block()
        }.onSuccess {
            //网络请求成功 关闭弹窗
            loadingChange.dismissDialog.postValue(false)
            //成功回调
            success?.invoke(it)
        }.onFailure {
            //网络请求异常 关闭弹窗
            loadingChange.dismissDialog.postValue(false)
            //打印错误消息
            LogUtils.e(it)
            //打印错误栈信息
            it.printStackTrace()
            //失败回调
            error(ExceptionHandle.handleException(it))
        }
    }
}

/**
 * 请求结果过滤，判断请求服务器请求结果是否成功，不成功则会抛出异常
 */
suspend fun <T> executeResponse(
    response: BaseResponse<T>,
    success: suspend CoroutineScope.(T) -> Unit,
) {
    coroutineScope {
        LogUtils.d(
            "LOGTAG==>",
            "response ${response.isSuccess()} ${response.getCode()} ${response.getMsg()}"
        )
        when {
            response.isSuccess() -> {
                success(response.getData())
            }
            else -> {
                throw AppException(response.getCode(), response.getMsg(), response.getMsg())
            }
        }
    }
}

/**
 *  调用携程
 * @param block 操作耗时操作任务
 * @param success 成功回调
 * @param error 失败回调 可不给
 */
fun <T> BaseViewModel.launch(
    block: suspend () -> T,
    success: (T) -> Unit,
    error: (Throwable) -> Unit = {},
) {
    viewModelScope.launch {
        kotlin.runCatching {
            withContext(Dispatchers.IO) {
                block()
            }
        }.onSuccess {
            success(it)
        }.onFailure {
            error(it)
        }
    }
}
